home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTAnchor.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  13.6 KB  |  532 lines

  1. /*    Hypertext "Anchor" Object                HTAnchor.c
  2. **    ==========================
  3. **
  4. ** An anchor represents a region of a hypertext document which is linked to
  5. ** another anchor in the same or a different document.
  6. **
  7. ** History
  8. **
  9. **         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
  10. **    24-Oct-1991 (JFG), written in C, browser-independant 
  11. **    21-Nov-1991 (JFG), first complete version
  12. **
  13. **    (c) Copyright CERN 1991 - See Copyright.html
  14. */
  15.  
  16. #define HASH_SIZE 101        /* Arbitrary prime. Memory/speed tradeoff */
  17.  
  18. #include <ctype.h>
  19. #include "tcp.h"
  20. #include "HTAnchor.h"
  21. #include "HTUtils.h"
  22. #include "HTParse.h"
  23.  
  24. typedef struct _HyperDoc Hyperdoc;
  25.  
  26. PRIVATE HTList **adult_table=0;  /* Point to table of lists of all parents */
  27.  
  28. /*                Creation Methods
  29. **                ================
  30. **
  31. **    Do not use "new" by itself outside this module. In order to enforce
  32. **    consistency, we insist that you furnish more information about the
  33. **    anchor you are creating : use newWithParent or newWithAddress.
  34. */
  35.  
  36. PRIVATE HTParentAnchor * HTParentAnchor_new
  37.   NOARGS
  38. {
  39.   HTParentAnchor *newAnchor = 
  40.     (HTParentAnchor *) calloc (1, sizeof (HTParentAnchor));  /* zero-filled */
  41.   newAnchor->parent = newAnchor;
  42.   return newAnchor;
  43. }
  44.  
  45. PRIVATE HTChildAnchor * HTChildAnchor_new
  46.   NOARGS
  47. {
  48.   return (HTChildAnchor *) calloc (1, sizeof (HTChildAnchor));  /* zero-filled */
  49. }
  50.  
  51.  
  52. /*    Case insensitive string comparison
  53. **    ----------------------------------
  54. ** On entry,
  55. **    s    Points to one string, null terminated
  56. **    t    points to the other.
  57. ** On exit,
  58. **    returns    YES if the strings are equivalent ignoring case
  59. **        NO if they differ in more than  their case.
  60. */
  61.  
  62. PRIVATE BOOL equivalent
  63.   ARGS2 (CONST char *,s, CONST char *,t)
  64. {
  65.   if (s && t) {  /* Make sure they point to something */
  66.     for ( ; *s && *t ; s++, t++) {
  67.         if (TOUPPER(*s) != TOUPPER(*t))
  68.       return NO;
  69.     }
  70.     return TOUPPER(*s) == TOUPPER(*t);
  71.   } else
  72.     return s == t;  /* Two NULLs are equivalent, aren't they ? */
  73. }
  74.  
  75.  
  76. /*    Create new or find old sub-anchor
  77. **    ---------------------------------
  78. **
  79. **    Me one is for a new anchor being edited into an existing
  80. **    document. The parent anchor must already exist.
  81. */
  82.  
  83. PUBLIC HTChildAnchor * HTAnchor_findChild
  84.   ARGS2 (HTParentAnchor *,parent, CONST char *,tag)
  85. {
  86.   HTChildAnchor *child;
  87.   HTList *kids;
  88.  
  89.   if (! parent) {
  90.     if (TRACE) printf ("HTAnchor_findChild called with NULL parent.\n");
  91.     return NULL;
  92.   }
  93.   if (kids = parent->children) {  /* parent has children : search them */
  94.     if (tag && *tag) {        /* TBL */
  95.     while (child = HTList_nextObject (kids)) {
  96.         if (equivalent(child->tag, tag)) { /* Case sensitive 920226 */
  97.         if (TRACE) fprintf (stderr,
  98.            "Child anchor %p of parent %p with name `%s' already exists.\n",
  99.             (void*)child, (void*)parent, tag);
  100.         return child;
  101.         }
  102.     }
  103.      }  /*  end if tag is void */
  104.   } else  /* parent doesn't have any children yet : create family */
  105.     parent->children = HTList_new ();
  106.  
  107.   child = HTChildAnchor_new ();
  108.   if (TRACE) fprintf(stderr, "new Anchor %p named `%s' is child of %p\n",
  109.        (void*)child, (int)tag ? tag : (CONST char *)"" , (void*)parent); /* int for apollo */
  110.   HTList_addObject (parent->children, child);
  111.   child->parent = parent;
  112.   StrAllocCopy(child->tag, tag);
  113.   return child;
  114. }
  115.  
  116.  
  117. /*    Create or find a child anchor with a possible link
  118. **    --------------------------------------------------
  119. **
  120. **    Create new anchor with a given parent and possibly
  121. **    a name, and possibly a link to a _relatively_ named anchor.
  122. **    (Code originally in ParseHTML.h)
  123. */
  124. PUBLIC HTChildAnchor * HTAnchor_findChildAndLink
  125.   ARGS4(
  126.        HTParentAnchor *,parent,    /* May not be 0 */
  127.        CONST char *,tag,    /* May be "" or 0 */
  128.        CONST char *,href,    /* May be "" or 0 */
  129.        HTLinkType *,ltype    /* May be 0 */
  130.        )
  131. {
  132.   HTChildAnchor * child = HTAnchor_findChild(parent, tag);
  133.   if (href && *href) {
  134.     char * relative_to = HTAnchor_address((HTAnchor *) parent);
  135.     char * parsed_address = HTParse(href, relative_to, PARSE_ALL);
  136.     HTAnchor * dest = HTAnchor_findAddress(parsed_address);
  137.     HTAnchor_link((HTAnchor *) child, dest, ltype);
  138.     free(parsed_address);
  139.     free(relative_to);
  140.   }
  141.   return child;
  142. }
  143.  
  144.  
  145. /*    Create new or find old named anchor
  146. **    -----------------------------------
  147. **
  148. **    Me one is for a reference which is found in a document, and might
  149. **    not be already loaded.
  150. **    Note: You are not guaranteed a new anchor -- you might get an old one,
  151. **    like with fonts.
  152. */
  153.  
  154. HTAnchor * HTAnchor_findAddress
  155.   ARGS1 (CONST char *,address)
  156. {
  157.   char *tag = HTParse (address, "", PARSE_ANCHOR);  /* Anchor tag specified ? */
  158.  
  159.   /* If the address represents a sub-anchor, we recursively load its parent,
  160.      then we create a child anchor within that document. */
  161.   if (tag && *tag) 
  162.     {
  163.       char *docAddress = HTParse(address, "", PARSE_ACCESS | PARSE_HOST |
  164.                                  PARSE_PATH | PARSE_PUNCTUATION);
  165.       HTParentAnchor * foundParent =
  166.         (HTParentAnchor *) HTAnchor_findAddress (docAddress);
  167.       HTChildAnchor * foundAnchor = HTAnchor_findChild (foundParent, tag);
  168.       free (docAddress);
  169.       free (tag);
  170.       return (HTAnchor *) foundAnchor;
  171.     }
  172.   
  173.   else { /* If the address has no anchor tag, 
  174.         check whether we have this node */
  175.     int hash;
  176.     CONST char *p;
  177.     HTList * adults;
  178.     HTList *grownups;
  179.     HTParentAnchor * foundAnchor;
  180.  
  181.     free (tag);
  182.     
  183.     /* Select list from hash table */
  184.     for(p=address, hash=0; *p; p++)
  185.         hash = (hash * 3 + (*(unsigned char*)p))
  186.          % HASH_SIZE;
  187.     if (!adult_table)
  188.         adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
  189.     if (!adult_table[hash]) adult_table[hash] = HTList_new();
  190.     adults = adult_table[hash];
  191.  
  192.     /* Search list for anchor */
  193.     grownups = adults;
  194.     while (foundAnchor = HTList_nextObject (grownups)) {
  195.        if (equivalent(foundAnchor->address, address)) {
  196.     if (TRACE) fprintf(stderr, "Anchor %p with address `%s' already exists.\n",
  197.               (void*) foundAnchor, address);
  198.     return (HTAnchor *) foundAnchor;
  199.       }
  200.     }
  201.     
  202.     /* Node not found : create new anchor */
  203.     foundAnchor = HTParentAnchor_new ();
  204.     if (TRACE) fprintf(stderr, "New anchor %p has hash %d and address `%s'\n",
  205.         (void*)foundAnchor, hash, address);
  206.     StrAllocCopy(foundAnchor->address, address);
  207.     HTList_addObject (adults, foundAnchor);
  208.     return (HTAnchor *) foundAnchor;
  209.   }
  210. }
  211.  
  212.  
  213. /*    Delete an anchor and possibly related things (auto garbage collection)
  214. **    --------------------------------------------
  215. **
  216. **    The anchor is only deleted if the corresponding document is not loaded.
  217. **    All outgoing links from parent and children are deleted, and this anchor
  218. **    is removed from the sources list of all its targets.
  219. **    We also try to delete the targets whose documents are not loaded.
  220. **    If this anchor's source list is empty, we delete it and its children.
  221. */
  222.  
  223. PRIVATE void deleteLinks
  224.   ARGS1 (HTAnchor *,me)
  225. {
  226.   if (! me)
  227.     return;
  228.  
  229.   /* Recursively try to delete target anchors */
  230.   if (me->mainLink.dest) {
  231.     HTParentAnchor *parent = me->mainLink.dest->parent;
  232.     HTList_removeObject (parent->sources, me);
  233.     if (! parent->document)  /* Test here to avoid calling overhead */
  234.       HTAnchor_delete (parent);
  235.   }
  236.   if (me->links) {  /* Extra destinations */
  237.     HTLink *target;
  238.     while (target = HTList_removeLastObject (me->links)) {
  239.       HTParentAnchor *parent = target->dest->parent;
  240.       HTList_removeObject (parent->sources, me);
  241.       if (! parent->document)  /* Test here to avoid calling overhead */
  242.     HTAnchor_delete (parent);
  243.     }
  244.   }
  245. }
  246.  
  247. PUBLIC BOOL HTAnchor_delete
  248.   ARGS1 (HTParentAnchor *,me)
  249. {
  250.   HTChildAnchor *child;
  251.  
  252.   /* Don't delete if document is loaded */
  253.   if (me->document)
  254.     return NO;
  255.  
  256.   /* Recursively try to delete target anchors */
  257.   deleteLinks ((HTAnchor *) me);
  258.  
  259.   if (! HTList_isEmpty (me->sources)) {  /* There are still incoming links */
  260.     /* Delete all outgoing links from children, if any */
  261.     HTList *kids = me->children;
  262.     while (child = HTList_nextObject (kids))
  263.       deleteLinks ((HTAnchor *) child);
  264.     return NO;  /* Parent not deleted */
  265.   }
  266.  
  267.   /* No more incoming links : kill everything */
  268.   /* First, recursively delete children */
  269.   while (child = HTList_removeLastObject (me->children)) {
  270.     deleteLinks ((HTAnchor *) child);
  271.     free (child->tag);
  272.     free (child);
  273.   }
  274.  
  275.   /* Now kill myself */
  276.   HTList_delete (me->children);
  277.   HTList_delete (me->sources);
  278.   free (me->address);
  279.   /* Devise a way to clean out the HTFormat if no longer needed (ref count?) */
  280.   free (me);
  281.   return YES;  /* Parent deleted */
  282. }
  283.  
  284.  
  285. /*        Move an anchor to the head of the list of its siblings
  286. **        ------------------------------------------------------
  287. **
  288. **    This is to ensure that an anchor which might have already existed
  289. **    is put in the correct order as we load the document.
  290. */
  291.  
  292. void HTAnchor_makeLastChild
  293.   ARGS1(HTChildAnchor *,me)
  294. {
  295.   if (me->parent != (HTParentAnchor *) me) {  /* Make sure it's a child */
  296.     HTList * siblings = me->parent->children;
  297.     HTList_removeObject (siblings, me);
  298.     HTList_addObject (siblings, me);
  299.   }
  300. }
  301.  
  302. /*    Data access functions
  303. **    ---------------------
  304. */
  305.  
  306. PUBLIC HTParentAnchor * HTAnchor_parent
  307.   ARGS1 (HTAnchor *,me)
  308. {
  309.   return me ? me->parent : NULL;
  310. }
  311.  
  312. void HTAnchor_setDocument
  313.   ARGS2 (HTParentAnchor *,me, HyperDoc *,doc)
  314. {
  315.   if (me)
  316.     me->document = doc;
  317. }
  318.  
  319. HyperDoc * HTAnchor_document
  320.   ARGS1 (HTParentAnchor *,me)
  321. {
  322.   return me ? me->document : NULL;
  323. }
  324.  
  325.  
  326. /* We don't want code to change an address after anchor creation... yet ?
  327. void HTAnchor_setAddress
  328.   ARGS2 (HTAnchor *,me, char *,addr)
  329. {
  330.   if (me)
  331.     StrAllocCopy (me->parent->address, addr);
  332. }
  333. */
  334.  
  335. char * HTAnchor_address
  336.   ARGS1 (HTAnchor *,me)
  337. {
  338.   char *addr = NULL;
  339.   if (me) {
  340.     if (((HTParentAnchor *) me == me->parent) ||
  341.         !((HTChildAnchor *) me)->tag) {  /* it's an adult or no tag */
  342.       StrAllocCopy (addr, me->parent->address);
  343.     }
  344.     else {  /* it's a named child */
  345.       addr = malloc (2 + strlen (me->parent->address)
  346.              + strlen (((HTChildAnchor *) me)->tag));
  347.       if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
  348.       sprintf (addr, "%s#%s", me->parent->address,
  349.            ((HTChildAnchor *) me)->tag);
  350.     }
  351.   }
  352.   return addr;
  353. }
  354.  
  355.  
  356.  
  357. void HTAnchor_setFormat
  358.   ARGS2 (HTParentAnchor *,me, HTFormat ,form)
  359. {
  360.   if (me)
  361.     me->format = form;
  362. }
  363.  
  364. HTFormat HTAnchor_format
  365.   ARGS1 (HTParentAnchor *,me)
  366. {
  367.   return me ? me->format : NULL;
  368. }
  369.  
  370.  
  371.  
  372. void HTAnchor_setIndex
  373.   ARGS1 (HTParentAnchor *,me)
  374. {
  375.   if (me)
  376.     me->isIndex = YES;
  377. }
  378.  
  379. BOOL HTAnchor_isIndex
  380.   ARGS1 (HTParentAnchor *,me)
  381. {
  382.   return me ? me->isIndex : NO;
  383. }
  384.  
  385.  
  386.  
  387. BOOL HTAnchor_hasChildren
  388.   ARGS1 (HTParentAnchor *,me)
  389. {
  390.   return me ? ! HTList_isEmpty(me->children) : NO;
  391. }
  392.  
  393. /*    Title handling
  394. */
  395. CONST char * HTAnchor_title
  396.   ARGS1 (HTParentAnchor *,me)
  397. {
  398.   return me ? me->title : 0;
  399. }
  400.  
  401. void HTAnchor_setTitle
  402.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  403. {
  404.   StrAllocCopy(me->title, title);
  405. }
  406.  
  407. void HTAnchor_appendTitle
  408.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  409. {
  410.   StrAllocCat(me->title, title);
  411. }
  412.  
  413. /*    Link me Anchor to another given one
  414. **    -------------------------------------
  415. */
  416.  
  417. BOOL HTAnchor_link
  418.   ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
  419. {
  420.   if (! (source && destination))
  421.     return NO;  /* Can't link to/from non-existing anchor */
  422.   if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
  423.   if (! source->mainLink.dest) {
  424.     source->mainLink.dest = destination;
  425.     source->mainLink.type = type;
  426.   } else {
  427.     HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
  428.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
  429.     newLink->dest = destination;
  430.     newLink->type = type;
  431.     if (! source->links)
  432.       source->links = HTList_new ();
  433.     HTList_addObject (source->links, newLink);
  434.   }
  435.   if (!destination->parent->sources)
  436.     destination->parent->sources = HTList_new ();
  437.   HTList_addObject (destination->parent->sources, source);
  438.   return YES;  /* Success */
  439. }
  440.  
  441.  
  442. /*    Manipulation of links
  443. **    ---------------------
  444. */
  445.  
  446. HTAnchor * HTAnchor_followMainLink
  447.   ARGS1 (HTAnchor *,me)
  448. {
  449.   return me->mainLink.dest;
  450. }
  451.  
  452. HTAnchor * HTAnchor_followTypedLink
  453.   ARGS2 (HTAnchor *,me, HTLinkType *,type)
  454. {
  455.   if (me->mainLink.type == type)
  456.     return me->mainLink.dest;
  457.   if (me->links) {
  458.     HTList *links = me->links;
  459.     HTLink *link;
  460.     while (link = HTList_nextObject (links))
  461.       if (link->type == type)
  462.     return link->dest;
  463.   }
  464.   return NULL;  /* No link of me type */
  465. }
  466.  
  467.  
  468. /*    Make main link
  469. */
  470. BOOL HTAnchor_makeMainLink
  471.   ARGS2 (HTAnchor *,me, HTLink *,movingLink)
  472. {
  473.   /* Check that everything's OK */
  474.   if (! (me && HTList_removeObject (me->links, movingLink)))
  475.     return NO;  /* link not found or NULL anchor */
  476.   else {
  477.     /* First push current main link onto top of links list */
  478.     HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
  479.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
  480.     memcpy (newLink, & me->mainLink, sizeof (HTLink));
  481.     HTList_addObject (me->links, newLink);
  482.  
  483.     /* Now make movingLink the new main link, and free it */
  484.     memcpy (& me->mainLink, movingLink, sizeof (HTLink));
  485.     free (movingLink);
  486.     return YES;
  487.   }
  488. }
  489.  
  490.  
  491. /*    Methods List
  492. **    ------------
  493. */
  494.  
  495. PUBLIC HTList * HTAnchor_methods ARGS1(HTParentAnchor *, me)
  496. {
  497.     if (!me->methods) {
  498.         me->methods = HTList_new();
  499.     }
  500.     return me->methods;
  501. }
  502.  
  503. /*    Protocol
  504. **    --------
  505. */
  506.  
  507. PUBLIC void * HTAnchor_protocol ARGS1(HTParentAnchor *, me)
  508. {
  509.     return me->protocol;
  510. }
  511.  
  512. PUBLIC void HTAnchor_setProtocol ARGS2(HTParentAnchor *, me,
  513.     void*,    protocol)
  514. {
  515.     me->protocol = protocol;
  516. }
  517.  
  518. /*    Physical Address
  519. **    ----------------
  520. */
  521.  
  522. PUBLIC char * HTAnchor_physical ARGS1(HTParentAnchor *, me)
  523. {
  524.     return me->physical;
  525. }
  526.  
  527. PUBLIC void HTAnchor_setPhysical ARGS2(HTParentAnchor *, me,
  528.     char *,    physical)
  529. {
  530.     StrAllocCopy(me->physical, physical);
  531. }
  532.